<div style="margin-bottom: 15px">
    <h3 class="first">{title}</h3>
    <p>{__('computed columns / modal / intro')}</p>

    <label>{__('computed columns / modal / name')}</label>
    <input type="text" bind:value="name" />

    <label>{__('computed columns / modal / formula')}</label>
    <textarea ref:code class="code"></textarea>

    <p style="margin-top: 1em">{__('computed columns / modal / available columns')}:</p>

    <ul class="col-select">
        {#each metaColumns as col}
        <li on:click="insert(col)">{col.key}</li>
        {/each}
    </ul>
</div>

<button on:click="removeColumn()" class="btn btn-danger"><i class="fa fa-trash"></i> {__('computed columns / modal / remove')}</button>

<style lang="less">
    label {
        font-weight: bold;
    }
    .col-select {
        padding: 0;
        margin: 0;
        li {
            font-family: 'Roboto mono';
            display: inline-block;
            /*color: #1d81a2;*/
            cursor: pointer;
            margin: 0px 1ex 1ex 0;
            font-size: 12px;
            line-height: 15px;
            background: #1d81a2;
            color: white;
            padding: 2px 5px;
            border-radius: 2px;

            &:hover {
                background: #18a1cd;
            }
        }
    }
    :global(.CodeMirror) {
        border-radius: 1px;
        width: 95%;
        height: 130px;
        padding: 0px 3px;
        border: 1px solid #cccccc;
        box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
        transition: border linear 0.2s, box-shadow linear 0.2s;
        background-color: #ffffff;

        .CodeMirror-nonmatchingbracket {
            color: inherit;
            background: #fcc;
        }

        .CodeMirror-matchingbracket {
            color: inherit;
            background: #cfc;
        }

        .CodeMirror-placeholder,
        .cm-s-default .cm-comment {
            color: #9c938b;
        }

        .cm-variable-2 {
            color: #18a1cd !important;
        }

        &.CodeMirror-focused {
            border-color: rgba(82, 168, 236, 0.8);
            outline: 0;
            outline: thin dotted \9;
            -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
            -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
            box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
        }
    }
</style>

<script>
    import CodeMirror from 'cm/lib/codemirror';
    import 'cm/mode/javascript/javascript';
    import 'cm/addon/mode/simple';
    import 'cm/addon/hint/show-hint';
    import 'cm/addon/edit/matchbrackets';
    import 'cm/addon/display/placeholder';

    import _debounce from 'underscore-es/debounce';
    import clone from '../shared/clone.js';
    import { __ } from '../shared/l10n';
    import columnNameToVariable from '../shared/columnNameToVariable';

    export default {
        data() {
            return {
                name: '',
                formula: ''
            };
        },
        computed: {
            title({ column }) {
                var s = __('describe / edit-column');
                return s.replace('%s', `"${column ? column.title() || column.name() : '--'}"`);
            },
            metaColumns({ columns }) {
                if (!columns) return [];
                return columns.map(col => {
                    return {
                        key: columnNameToVariable(col.name()),
                        title: col.title(),
                        type: col.type()
                    };
                });
            },
            keywords({ metaColumns }) {
                const keywords = ['sum', 'round', 'min', 'max', 'median', 'mean'];
                metaColumns.forEach(function(col) {
                    keywords.push(col.key);
                    if (col.type === 'number') {
                        keywords.push(col.key + '__sum');
                        keywords.push(col.key + '__min');
                        keywords.push(col.key + '__max');
                        keywords.push(col.key + '__mean');
                        keywords.push(col.key + '__median');
                    }
                });
                return keywords;
            }
        },
        helpers: { __ },
        methods: {
            insert(column) {
                const { cm } = this.get();
                cm.replaceSelection(column.key);
                cm.focus();
            },
            removeColumn() {
                const { column } = this.get();
                const chart = this.store.get('dw_chart');
                const ds = chart.dataset();
                const customCols = clone(chart.get('metadata.describe.computed-columns', {}));
                delete customCols[column.name()];
                const colIndex = ds.columnOrder()[ds.indexOf(column.name())];
                // delete all changes that have been made to this column
                const changes = chart.get('metadata.data.changes', []);
                const newChanges = [];
                changes.forEach(c => {
                    if (c.column === colIndex) return; // skip
                    if (c.column > colIndex) c.column--;
                    newChanges.push(c);
                });
                chart.set('metadata.describe.computed-columns', customCols);
                chart.set('metadata.data.changes', newChanges);
                chart.saveSoon();
                this.fire('updateTable');
                this.fire('unselect');
            }
        },

        oncreate() {
            const { column } = this.get();

            const chart = this.store.get('dw_chart');
            const customCols = chart.get('metadata.describe.computed-columns', {});

            this.set({
                formula: customCols[column.name()] || '',
                name: column.title()
            });

            // update if column changes
            this.observe('column', col => {
                if (col)
                    this.set({
                        formula: customCols[col.name()] || '',
                        name: col.title()
                    });
            });

            const app = this;

            function scriptHint(editor) {
                // Find the token at the cursor
                const cur = editor.getCursor();
                const token = editor.getTokenAt(cur);
                let match = [];

                const keywords = app.get('keywords');

                if (token.type === 'variable') {
                    match = keywords.filter(function(chk) {
                        return chk.toLowerCase().indexOf(token.string.toLowerCase()) === 0;
                    });
                }

                return {
                    list: match,
                    from: CodeMirror.Pos(cur.line, token.start),
                    to: CodeMirror.Pos(cur.line, token.end)
                };
            }

            // CodeMirror.registerHelper("hint", "javascript", function(editor, options) {
            //     return scriptHint(editor, options);
            // });

            const cm = CodeMirror.fromTextArea(this.refs.code, {
                value: this.get('formula') || '',
                mode: 'simple',
                indentUnit: 2,
                tabSize: 2,
                lineWrapping: true,
                matchBrackets: true,
                placeholder: '// enter formula here',
                continueComments: 'Enter',
                extraKeys: {
                    Tab: 'autocomplete'
                },
                hintOptions: {
                    hint: scriptHint
                }
            });

            window.CodeMirror = CodeMirror;

            this.set({ cm });

            const updateTable = _debounce(() => this.fire('updateTable'), 1500);

            this.observe('formula', formula => {
                // update codemirror
                if (formula !== cm.getValue()) {
                    cm.setValue(formula);
                }
                // update dw.chart
                const { column } = this.get();
                const customCols = clone(chart.get('metadata.describe.computed-columns', {}));
                if (customCols[column.name()] !== formula) {
                    customCols[column.name()] = formula;
                    chart.set('metadata.describe.computed-columns', customCols);
                    if (chart.saveSoon) chart.saveSoon();
                    updateTable();
                }
            });

            this.observe('name', name => {
                const { column } = this.get();
                const changes = clone(chart.get('metadata.data.changes', []));
                const ds = chart.dataset();
                const col = ds.columnOrder()[ds.indexOf(column.name())];
                let lastColNameChangeIndex = -1;
                changes.forEach((change, i) => {
                    if (change.column === col && change.row === 0) {
                        lastColNameChangeIndex = i;
                    }
                });
                if (lastColNameChangeIndex > -1) {
                    // update last change of that cell
                    changes[lastColNameChangeIndex].value = name;
                    changes[lastColNameChangeIndex].time = new Date().getTime();
                } else {
                    // add new change
                    changes.push({
                        column: col,
                        row: 0,
                        value: name,
                        time: new Date().getTime()
                    });
                }
                chart.set('metadata.data.changes', changes);
                if (chart.saveSoon) chart.saveSoon();
                updateTable();
            });

            cm.on('change', cm => {
                this.set({ formula: cm.getValue() });
            });

            this.observe('metaColumns', cols => {
                var columnsRegex = new RegExp(`(?:${this.get('keywords').join('|')})`);
                CodeMirror.defineSimpleMode('simplemode', {
                    // The start state contains the rules that are intially used
                    start: [
                        // The regex matches the token, the token property contains the type
                        { regex: /"(?:[^\\]|\\.)*?(?:"|$)/, token: 'string' },
                        // You can match multiple tokens at once. Note that the captured
                        // groups must span the whole string in this case
                        {
                            regex: /(function)(\s+)([a-z$][\w$]*)/,
                            token: ['keyword', null, 'keyword']
                        },
                        // Rules are matched in the order in which they appear, so there is
                        // no ambiguity between this one and the one above
                        {
                            regex: /(?:function|var|return|if|for|while|else|do|this)\b/,
                            token: 'keyword'
                        },
                        { regex: /true|false|null|undefined/, token: 'atom' },
                        {
                            regex: /0x[a-f\d]+|[-+]?(?:\.\d+|\d+\.?\d*)(?:e[-+]?\d+)?/i,
                            token: 'number'
                        },
                        { regex: /\/\/.*/, token: 'comment' },
                        { regex: /\/(?:[^\\]|\\.)*?\//, token: 'variable-3' },
                        // A next property will cause the mode to move to a different state
                        { regex: /\/\*/, token: 'comment', next: 'comment' },
                        { regex: /[-+/*=<>!]+/, token: 'operator' },
                        // indent and dedent properties guide autoindentation
                        { regex: /[{[(]/, indent: true },
                        { regex: /[}\])]/, dedent: true },
                        { regex: columnsRegex, token: 'variable-2' },
                        { regex: /[a-z$][\w$]*/, token: 'variable' },
                        // You can embed other modes with the mode property. This rule
                        // causes all code between << and >> to be highlighted with the XML
                        // mode.
                        { regex: /<</, token: 'meta', mode: { spec: 'xml', end: />>/ } }
                    ],
                    // The multi-line comment state.
                    comment: [{ regex: /.*?\*\//, token: 'comment', next: 'start' }, { regex: /.*/, token: 'comment' }],
                    // The meta property contains global information about the mode. It
                    // can contain properties like lineComment, which are supported by
                    // all modes, and also directives like dontIndentStates, which are
                    // specific to simple modes.
                    meta: {
                        dontIndentStates: ['comment'],
                        lineComment: '//'
                    }
                });

                cm.setOption('mode', 'simplemode');
            });
        }
    };
</script>
